#include "stdafx.h"
#include "GameState.h"
#include "RenderSystem.h"
#include "ResourceManager.h"
#include "InputManager.h"
#include "EntityManager.h"
#include "SceneManager.h"
#include "ParticleRenderer.h"
#include "EntityFactory.h"
#include "Camera.h"
#include "CollisionManager.h"
#include "AudioSystem.h"
#include "CameraControl.h"
#include "Grid.h"
#include "EnemySpawner.h"
#include "Wall.h"

#include "Level.h"
GameState::GameState(void) 
	: m_collision_manager(nullptr), m_entity_factory(nullptr), m_entity_manager(nullptr), m_camera_control(nullptr), m_grid(nullptr)
{
}
GameState::~GameState(void)
{
	delete m_entity_factory;
	m_entity_factory = nullptr;
}

static bool entity_factory_created = false; //Fulhack

void GameState::Init(){
	m_input_manager = m_systems->m_input_manager;
	m_render_system = m_systems->m_render_system;
	m_gui_manager = m_systems->m_gui_manager; 
	m_scene_manager = m_systems->m_scene_manager;

	m_collision_manager = new CollisionManager;
	m_collision_manager->Init();
	m_entity_manager = new EntityManager(m_scene_manager);
	if (!entity_factory_created)
	{
		m_entity_factory = new EntityFactory(m_render_system, m_systems->m_resource_manager, m_scene_manager);
		entity_factory_created = true;

		m_systems->m_resource_manager->GetModel("n64.obj");
		m_systems->m_resource_manager->GetModel("projectile_normal.obj");
		m_systems->m_resource_manager->GetModel("selector.obj");
		m_systems->m_resource_manager->GetModel("slowing_tower.obj");
		m_systems->m_resource_manager->GetModel("Tower.obj");
		m_systems->m_resource_manager->GetModel("tree.obj");
		m_systems->m_resource_manager->GetModel("wall_test.obj");
	}

	m_entity_factory->SetEntityManager(m_entity_manager);
	m_entity_factory->CreateMediators();
	m_entity_factory->SetCollisionManager(m_collision_manager);

	float width=m_systems->m_config_file->HasKey("width")?m_systems->m_config_file->AsFloat("width"):1280;
	float height=m_systems->m_config_file->HasKey("height")?m_systems->m_config_file->AsFloat("height"):720;

	m_game_time = 60.0f;

	m_camera = m_systems->m_camera;
	//m_camera->SetPerspective(width/height,DirectX::XM_PIDIV4,m_systems->m_config_file->AsFloat("c_znear"),m_systems->m_config_file->AsFloat("c_zfar"));
	m_camera_control=new CameraControl(m_camera);
	m_camera_control->Init(m_systems->m_message_system);
	m_camera_control->Settings(m_systems->m_config_file->AsFloat("c_speed"),m_systems->m_config_file->AsFloat("m_yaw"),m_systems->m_config_file->AsFloat("m_pitch"),m_systems->m_config_file->AsFloat("m_sensitivity"));

	m_camera->SetFreeCamera(false);
	if (!m_camera->IsFreeCamera())
	{
		m_camera->SetPosition(Vec3(30.0f, 40.0f, -20.0f));
		m_camera->RotateX(Math::ToRad(45.0f));
		m_camera->SetBoundingArea(70.0f, -20.0f, -10.0f, 90.0f);
	}

	// skydome
	Skydome *skydome=new Skydome;
	skydome->Init(m_render_system);
	m_scene_manager->SetSkydome(skydome);

	// heightmap
	Heightmap *heightmap=new Heightmap;
	heightmap->Create(m_render_system,"../data/texture/heightmap2.tga");
	m_scene_manager->SetHeightmap(heightmap);
	

	m_terrain_vertices = heightmap->GetVertices();



	// Game gui
	m_game_gui = new GameGUI(m_gui_manager);
	
	m_game_gui->CreateButton(Gui::EButtonType::BASIC_TOWER_SELECT, 10, 10, 128, 128, "80", 0x00000000, 7);
	m_game_gui->CreateButton(Gui::EButtonType::SLOWING_TOWER_SELECT, 10, 148, 128, 128, "125", 0x00000000, 7);

	m_game_gui->CreateTextfield(
		ETextfield_Type::TIME_ELAPSED,
		m_render_system->GetWidth() - 210,
		5, 
		0.14f * m_render_system->GetWidth(),
		0.044 * m_render_system->GetHeight(),
		"ELAPSED TIME: ",
		0x00000000, 
		7, 
		true);
	m_game_gui->CreateTextfield(ETextfield_Type::GOLD_COUNTER, 
		m_render_system->GetWidth() - 210,
		55,
		0.14f * m_render_system->GetWidth(),
		0.044 * m_render_system->GetHeight(), 
		"GOLD: ", 
		0x00000000, 
		7, 
		true);
	m_game_gui->CreateTextfield(
		ETextfield_Type::WALL_HEALTH,
		m_render_system->GetWidth() - 210,
		105, 
		0.14f * m_render_system->GetWidth(), 
		0.044 * m_render_system->GetHeight(), 
		"WALL HP: ", 
		0x00000000, 
		7,
		true);

	m_game_gui->CreateTextfield(
		ETextfield_Type::WIN,
		(m_render_system->GetWidth() / 2) - 200, 
		200,
		0.28f * m_render_system->GetWidth(), 
		0.18f * m_render_system->GetHeight(), 
		"YOU WIN!", 
		0x00000000, 
		24, 
		false);
	m_game_gui->GetTextfield(ETextfield_Type::WIN)->SetTextPosition(Vector2(40, 100));

	m_game_gui->CreateTextfield(
		ETextfield_Type::LOSE, 
		(m_render_system->GetWidth() / 2) - 200, 
		200,
		0.28f * m_render_system->GetWidth(), 
		0.18f * m_render_system->GetHeight(), 
		"YOU LOSE!", 
		0x00000000, 
		24,
		false);
	m_game_gui->GetTextfield(ETextfield_Type::LOSE)->SetTextPosition(Vector2(20, 100));

	m_level = new Level;
	m_level->Init("../data/level/level.TDLEVEL", m_entity_manager, m_entity_factory);

	m_player = new Player(m_input_manager, m_entity_manager, m_entity_factory, m_game_gui, m_terrain_vertices, m_level->GetGrid());	
	m_grid = m_level->GetGrid();

	m_selector = m_entity_factory->Create(N64_Logo, m_grid);
	m_entity_manager->AddEntity(m_selector);


	Gui::Button* btn = m_game_gui->GetButton(Gui::EButtonType::BASIC_TOWER_SELECT);
	btn->SetTexcoords(0.1875f, 0.0f, 0.1875f, 0.1875f);
	btn->SetTextPosition(Vector2(btn->GetWidth() - 35, 100));

	btn = m_game_gui->GetButton(Gui::EButtonType::SLOWING_TOWER_SELECT);
	btn->SetTexcoords((float)6/(float)16, 0.0f, 0.1875f, 0.1875f);
	btn->SetTextPosition(Vector2(btn->GetWidth() - 45, 100));

	Gui::Textfield* field = m_game_gui->GetTextfield(ETextfield_Type::TIME_ELAPSED);
	field->SetTexcoords(0.0f, (float)15/(float)16, (float)6/(float)16, (float)1/(float)16);
	field->SetTextPosition(Vector2(0.0f, 10.0f));

	field = m_game_gui->GetTextfield(ETextfield_Type::GOLD_COUNTER);
	field->SetTexcoords(0.0f, (float)15/(float)16, (float)6/(float)16, (float)1/(float)16);
	field->SetTextPosition(Vector2(0.0f, 10.0f));

	field = m_game_gui->GetTextfield(ETextfield_Type::WALL_HEALTH);
	field->SetTexcoords(0.0f, (float)15/(float)16, (float)6/(float)16, (float)1/(float)16);
	field->SetTextPosition(Vector2(0.0f, 10.0f));

	field = m_game_gui->GetTextfield(ETextfield_Type::WIN);
	field->SetTexcoords(0.0f, (float)15/(float)16, (float)6/(float)16, (float)1/(float)16);

	field = m_game_gui->GetTextfield(ETextfield_Type::LOSE);
	field->SetTexcoords(0.0f, (float)15/(float)16, (float)6/(float)16, (float)1/(float)16);

	m_wall = m_entity_factory->Create(Wall_Type, m_grid);
	m_entity_manager->AddEntity(m_wall);
	if (m_wall->GetEntityType() == Wall_Type)
	{
		Wall* w =static_cast<Wall*>(m_wall);
		w->SetGridPosition(15, 0);
	}

	m_enemy_spawner = m_entity_factory->Create(Enemy_Spawner, m_grid);
	m_entity_manager->AddEntity(m_enemy_spawner);
	Grid::Square square = m_grid->GetSquare(6,30);
	m_enemy_spawner->SetPosition(square.m_position);
	if (m_enemy_spawner->GetEntityType() == Enemy_Spawner)
	{
		EnemySpawner* es =static_cast<EnemySpawner*>(m_enemy_spawner);
		square = m_grid->GetSquare(6, 19);
		es->AddWaypoint(Vector3(square.m_position.x, square.m_position.y + 1.3f, square.m_position.z));

		square = m_grid->GetSquare(10, 15);
		es->AddWaypoint(Vector3(square.m_position.x, square.m_position.y + 1.3f, square.m_position.z));

		square = m_grid->GetSquare(14, 15);
		es->AddWaypoint(Vector3(square.m_position.x, square.m_position.y + 1.3f, square.m_position.z));

		square = m_grid->GetSquare(14, 2);
		es->AddWaypoint(Vector3(square.m_position.x, square.m_position.y + 1.3f, square.m_position.z));
	}

	m_enemy_spawner2 = m_entity_factory->Create(Enemy_Spawner, m_grid);
	m_entity_manager->AddEntity(m_enemy_spawner2);
	square = m_grid->GetSquare(25,30);
	m_enemy_spawner2->SetPosition(square.m_position);
	if (m_enemy_spawner2->GetEntityType() == Enemy_Spawner)
	{
		EnemySpawner* es =static_cast<EnemySpawner*>(m_enemy_spawner2);
		square = m_grid->GetSquare(25, 19);
		es->AddWaypoint(Vector3(square.m_position.x, square.m_position.y + 1.3f, square.m_position.z));

		square = m_grid->GetSquare(21, 15);
		es->AddWaypoint(Vector3(square.m_position.x, square.m_position.y + 1.3f, square.m_position.z));

		square = m_grid->GetSquare(16, 15);
		es->AddWaypoint(Vector3(square.m_position.x, square.m_position.y + 1.3f, square.m_position.z));

		square = m_grid->GetSquare(16, 2);
		es->AddWaypoint(Vector3(square.m_position.x, square.m_position.y + 1.3f, square.m_position.z));
	}

	// Particle
	//m_particle_emitter = new ParticleEmitter(m_systems->m_resource_manager, m_render_system, m_scene_manager);
	//m_particle_emitter->Init(Vector3(0.0f, 0.0f, 0.0f), 0.001f, 4096);

	
	m_camera->SetSoundListener(m_systems->m_audio_system);
	m_sound3D = m_systems->m_resource_manager->GetSound3D("jaguar.wav");
	m_sound3D->SetPosition(0, 0.0f, 0.0f);
	//m_sound2D = m_systems->m_resource_manager->GetSound2D("drumloop.wav");
	//m_sound2D->SetLoop(true);
	//m_sound2D->Play();
	//m_music.Load("../data/audio/mm_menu02.ogg");
	//m_music.SetLoop(true);
	//m_music.Play();
	m_collision_manager->SetCollisionMethod(ECollisionMethod::COLL_BRUTE_FORCE);
	m_background_song_1.Load("../data/audio/game_song.ogg");
	m_background_song_1.SetLoop(true);
	m_background_song_1.SetVolume(0.5f);
	m_background_song_1.Play();
}

void GameState::Shut(){
	if (m_player){
		delete m_player;
		m_player = nullptr;
	}
	if (m_level) {
		m_level->Shut();
		delete m_level;
		m_level = nullptr;
	}
	if (m_camera_control) {
		m_camera_control->Shut(m_systems->m_message_system);
		delete m_camera_control;
		m_camera_control = nullptr;
	}
	if (m_entity_manager) {
		delete m_entity_manager;
		m_entity_manager = nullptr;
	}
	if (m_entity_factory) {
		m_entity_factory->SetEntityManager(nullptr);
		m_entity_factory->RemoveMediators();
		m_entity_factory->SetCollisionManager(nullptr);
	}
	if (m_collision_manager) {
		delete m_collision_manager;
		m_collision_manager = nullptr;
	}
	if (m_game_gui)
	{
		m_systems->m_gui_manager->ClearAllWidgets();
		delete m_game_gui;
		m_game_gui = nullptr;
	}
	m_scene_manager->Shut();

	m_background_song_1.Pause();
}

void GameState::Update(float deltatime){
	m_camera_control->Update(deltatime);

	static bool game_over = false;

	//bool inGui = false;
	

	// Update elapsed time
	m_game_gui->GetTextfield(ETextfield_Type::TIME_ELAPSED)->SetText("TIME: " + ToString((int)m_game_time));
	if (m_game_time <= 0.0f)
	{
		m_game_gui->GetTextfield(ETextfield_Type::WIN)->SetVisible(true);
		game_over = true;
	}

	if (m_wall->GetEntityType() == Wall_Type)
	{
		Wall* wall = static_cast<Wall*>(m_wall);
		m_game_gui->GetTextfield(ETextfield_Type::WALL_HEALTH)->SetText("WALL HP: " + ToString( wall->GetHealth() ) );
		if (wall->GetHealth() <= 0)
		{
			m_game_gui->GetTextfield(ETextfield_Type::LOSE)->SetVisible(true);
			game_over = true;
		}
	}

	if (!game_over)
	{
		m_game_time -= deltatime;

		Pick(m_input_manager->m_mouse.m_position.x, m_input_manager->m_mouse.m_position.y);

		m_player->AddGold(1);
		m_player->Update(m_raycast_hit);

		m_selector->SetPosition(m_raycast_hit);


		//if (m_input_manager->ButtonPressed(MB_Left))
		//{
		//	Entity* ent = m_entity_factory->Create(EEntityType::Basic_Tower, m_grid);
		//	ent->SetPosition(m_raycast_hit);
		//	m_entity_manager->AddEntity(ent);
		//	//ent->SetEntityListener(m_entity_manager);
		//}



		//m_particle_emitter->Update(deltatime, &m_camera);

		m_entity_manager->Update(deltatime);
		m_collision_manager->Update();

		//m_sound->SetPosition(m_selector->GetPosition());

		//static float timer = 0.0f;
		//timer += deltatime;
		//if (timer >= 3.0f){
		//	m_sound3D->Play();
		//	m_sound2D->Play();
		//	timer = 0.0f;
		//}
	}
	else
	{
		if (m_input_manager->ButtonReleased(EMouseButton::MB_Left) ||
			m_input_manager->ButtonReleased(EMouseButton::MB_Right) ||
			m_input_manager->KeyReleased(EKeyCode::KC_Return) ||
			m_input_manager->KeyReleased(EKeyCode::KC_Space))
		{
			game_over = false;
			m_camera->RotateX(Math::ToRad(-45.0f));
			ChangeState(FindById("MenuState"));
		}
	}
	m_gui_manager->Update();
}

void GameState::Render()
{
	m_render_system->Clear();

	m_scene_manager->Render(m_render_system, m_camera);
	m_gui_manager->Render();

	m_render_system->Present();
}

void GameState::Pick(float x, float y)
{
	using namespace DirectX;

	Mat4 proj = m_camera->GetProj();
	Mat4 view = m_camera->GetView();
	Mat4 world = DirectX::XMMatrixIdentity();

	XMVECTOR origin = XMVectorSet(x, y, 0.0f, 1.0f);
	origin = XMVector3Unproject(origin, 0.0f, 0.0f, 1440.0f, 900.0f, 0.0f, 1.0f, proj, view, world);
	
	XMVECTOR camera_pos(XMLoadFloat3(&m_camera->GetPosition()));

	XMVECTOR direction(origin - camera_pos);
	direction = XMVector3Normalize(direction);

	bool found = false;

	for (int y = 0; y < 32 - 1; y++)
	{
		for (int x = 0; x < 32 - 1; x++)
		{
			int topLeft = (32 * y) + x;
			int topRight = (32 * y) + (x + 1);
			int botLeft = (32 * (y + 1)) + x;
			int botRight = (32 * (y + 1)) + (x + 1);

			Heightmap::Vertex A = m_terrain_vertices[topLeft];
			Heightmap::Vertex B = m_terrain_vertices[topRight];
			Heightmap::Vertex C = m_terrain_vertices[botLeft];
			Heightmap::Vertex D = m_terrain_vertices[botRight];

			Vec3 v0pos = Vec3(A.position.x, A.position.y, A.position.z);
			XMVECTOR v0 = XMLoadFloat3(&v0pos);

			Vec3 v1pos = Vec3(B.position.x, B.position.y, B.position.z);
			XMVECTOR v1 = XMLoadFloat3(&v1pos);

			Vec3 v2pos = Vec3(C.position.x, C.position.y, C.position.z);
			XMVECTOR v2 = XMLoadFloat3(&v2pos);

			Vec3 v3pos = Vec3(D.position.x, D.position.y, D.position.z);
			XMVECTOR v3 = XMLoadFloat3(&v3pos);

			float d = 0.0f;

			if (TriangleTests::Intersects(origin, direction, v0, v1, v2, d) || TriangleTests::Intersects(origin, direction, v2, v1, v3, d))
			{
				// hit_position = place in world where the ray intersected triangle
				XMVECTOR hit_position = origin + direction * d;
				Vec3 h_p;
				XMStoreFloat3(&h_p, hit_position);

				//Entity* ent = m_entity_factory->Create(EEntityType::Basic_Tower);
				//ent->SetPosition(Vector3(h_p.x, h_p.y, h_p.z));
				//m_entity_manager->AddEntity(ent);

				// exact position
				//m_raycast_hit = Vector3(h_p.x, h_p.y, h_p.z);

				// Place in middle of a quad
				Vec3 top_left, top_right, bot_left, bot_right;
				XMStoreFloat3(&top_left, v0);
				XMStoreFloat3(&top_right, v1);
				XMStoreFloat3(&bot_left, v2);
				XMStoreFloat3(&bot_right, v3);

				m_raycast_hit.x = bot_left.x + ((bot_right.x - bot_left.x) * 0.5f);
				m_raycast_hit.y = h_p.y;
				m_raycast_hit.z = bot_left.z + ((top_left.z - bot_left.z) * 0.5f);

				found = true;
				break;
			}
		}
		if (found)
			break;
	}
}